package com.dou.sample.opengl_project.objects;

import android.opengl.GLES20;

import com.dou.sample.opengl_project.utls.GeoMetry;

import java.util.ArrayList;
import java.util.List;

import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
import static android.opengl.GLES20.glDrawArrays;

/**
 * Created by Dou on 2018/1/16.
 */

public class ObjectBuilder {

    public interface DrawCommand{
        void draw();
    }

    public class GenerateData{
        public float[] vertexData;
        public List<DrawCommand> drawList;

        public GenerateData(float[] vertexData, List<DrawCommand> drawList){
            this.vertexData = vertexData;
            this.drawList = drawList;
        }
    }

    private final List<DrawCommand> drawList = new ArrayList<>();

    private  int offset = 0;
    private float[] vertexData;
    private static final int FLOATS_PER_VERTEX = 3;

    public ObjectBuilder(int sizeInVertices){
        vertexData = new float[sizeInVertices * FLOATS_PER_VERTEX];
    }

    public static int sizeOfCircleInVertices(int numPoints){
        // 加上顶点和闭合的点
        return (numPoints + 1) + 1;
    }

    public static int sizeOfCylineInVertices(int numPoints){
        //
        return (numPoints + 1) * 2;
    }

    public void appendCircle(GeoMetry.Circle circle, int pointNum){

        final int startVertex = offset / FLOATS_PER_VERTEX;
        final int numVertex = sizeOfCircleInVertices(pointNum);

        vertexData[offset++] = circle.center.x;
        vertexData[offset++] = circle.center.y;
        vertexData[offset++] = circle.center.z;

        for (int i = 0; i <= pointNum; i++){
            float angleInRadius = ((float) i / (float) pointNum) * ((float) Math.PI * 2f);

            vertexData[offset++] = circle.center.x + circle.radius * (float) Math.cos(angleInRadius);
            vertexData[offset++] = circle.center.y;
            vertexData[offset++] = circle.center.z + circle.radius * (float) Math.sin(angleInRadius);
        }

        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GLES20.GL_TRIANGLE_FAN, startVertex, numVertex);
            }
        });
    }

    public void appendOpenCylinder(GeoMetry.Cylinder cylinder, int pointNum){
        final int startVertex = offset / FLOATS_PER_VERTEX;
        final int numVertex = sizeOfCylineInVertices(pointNum);

        for (int i = 0; i <= pointNum; i++){
            float angelInRadius = ((float) i / (float) pointNum) * ((float)Math.PI * 2f);

            float xPosition = cylinder.center.x + cylinder.radius * (float) (Math.cos(angelInRadius));
            float zPosition = cylinder.center.z + cylinder.radius * (float) (Math.sin(angelInRadius));

            float yTop = cylinder.center.y + cylinder.height / 2;
            float yBottom = cylinder.center.y - cylinder.height / 2;

            vertexData[offset++] = xPosition;
            vertexData[offset++] = yTop;
            vertexData[offset++] = zPosition;

            vertexData[offset++] = xPosition;
            vertexData[offset++] = yBottom;
            vertexData[offset++] = zPosition;
        }

        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startVertex, numVertex);
            }
        });
    }

    public GenerateData build(){
        return new GenerateData(vertexData, drawList);
    }

    public static GenerateData createPuck(GeoMetry.Cylinder cylinder, int numPoint){
        int size = sizeOfCircleInVertices(numPoint) + sizeOfCylineInVertices(numPoint);

        ObjectBuilder objectBuilder = new ObjectBuilder(size);

        GeoMetry.Circle puckTop = new GeoMetry.Circle(cylinder.center.translateY(cylinder.height / 2f), cylinder.radius);

        objectBuilder.appendCircle(puckTop, numPoint);
        objectBuilder.appendOpenCylinder(cylinder, numPoint);

        return objectBuilder.build();
    }

    public static GenerateData createMallet(GeoMetry.Point center, float radius, float height, int numPoint){
        int size = sizeOfCircleInVertices(numPoint) * 2 + sizeOfCylineInVertices(numPoint) * 2;

        ObjectBuilder objectBuilder = new ObjectBuilder(size);

        float baseHeight = height * 0.25f;

        GeoMetry.Circle baseCircle = new GeoMetry.Circle(center.translateY(-baseHeight), radius);
        GeoMetry.Cylinder baseCylinder = new GeoMetry.Cylinder(baseCircle.center.translateY(-baseHeight / 2f), radius, baseHeight);

        objectBuilder.appendCircle(baseCircle, numPoint);
        objectBuilder.appendOpenCylinder(baseCylinder, numPoint);

        float handleHeight = height * 0.75f;
        float handleRadius = radius / 3f;

        GeoMetry.Circle handleCircle = new GeoMetry.Circle(center.translateY(height * 0.5f), handleRadius);
        GeoMetry.Cylinder handleCylinder = new GeoMetry.Cylinder(handleCircle.center.translateY(-handleHeight / 2), handleRadius, handleHeight);

        objectBuilder.appendCircle(handleCircle, numPoint);
        objectBuilder.appendOpenCylinder(handleCylinder, numPoint);

        return objectBuilder.build();
    }
}
